﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using MvcEngine.Core.Repositories;
using MvcEngine.Core.Utils;
using MvcEngine.Core.Exceptions;
using MvcEngine.Core.Extensions;
using MvcEngine.Core.Pagination;
using MvcEngine.Core.Base;
using MvcEngine.Core.Helpers;

namespace MvcEngine.Core.Account
{
    public class AccountManager : IAccountManager
    {
        private readonly IUserRepository userRepository = null;
        private readonly IRoleRepository roleRepository = null;
        private readonly IPermissionRepository permissionRepository = null;

        public static IAccountManager Current
        {
            get { return IoC.Resolve<IAccountManager>(); }
        }

        public AccountManager(IUserRepository userRepository, IRoleRepository roleRepository, IPermissionRepository permissionRepository)
        {
            this.userRepository = userRepository;
            this.roleRepository = roleRepository;
            this.permissionRepository = permissionRepository;
        }

        public IPagination<User> GetUsers(LoadOptions options)
        {
            return CacheManager.Current.FromCache(string.Format(CacheKey.AccountKeys.UserPaging, SiteContext.Current.Site.Id, options.PageNumber, options.PageSize), CacheKey.AccountKeys.UserCategory,
                    () => userRepository.GetAll(SiteContext.Current.Site.Id, options));
        }

        public User GetUser(Guid userId)
        {
            return CacheManager.Current.FromCache(string.Format(CacheKey.AccountKeys.UserId, userId), CacheKey.AccountKeys.UserCategory,
                () => userRepository.Get(userId));
        }

        public User GetUser(string userName)
        {
            return CacheManager.Current.FromCache(string.Format(CacheKey.AccountKeys.UserName, SiteContext.Current.Site.Id, userName), CacheKey.AccountKeys.UserCategory,
                () => userRepository.Get(userName, SiteContext.Current.Site.Id));
        }

        public void SaveUser(User user)
        {
            Guard.ThrowIfEmptyGuid(user.Id);

            using (var unitOfWork = UnitOfWorkHelper.Get())
            {
                var oldItem = userRepository.Get(user.Id);
                if (oldItem != null)
                {
                    oldItem.ModifiedDate = DateTime.Now;
                    if (UserContext.Current.User != null)
                        oldItem.ModifiedBy = UserContext.Current.User.Id;
                    oldItem.Password = user.Password;
                    oldItem.PasswordSalt = user.PasswordSalt;
                    oldItem.CreateGuid = user.CreateGuid;
                    oldItem.RestoreGuid = user.RestoreGuid;
                    oldItem.FailedPasswordAnswerAttemptCount = user.FailedPasswordAnswerAttemptCount;
                    oldItem.FailedPasswordAttemptCount = user.FailedPasswordAttemptCount;
                    oldItem.IsHost = user.IsHost;
                    oldItem.IsLockedOut = user.IsLockedOut;
                    oldItem.IsApproved = user.IsApproved;
                    userRepository.Update(oldItem);
                }
                else
                {
                    string salt = HashHelper.Current.GenerateHashString();
                    user.Name = user.Email;
                    user.SiteId = SiteContext.Current.Site.Id;
                    user.CreatedDate = DateTime.Now;
                    user.ModifiedDate = DateTime.Now;
                    user.CreateGuid = Guid.NewGuid();
                    user.RestoreGuid = Guid.NewGuid();
                    user.IsApproved = false;
                    user.PasswordSalt = salt;
                    user.Password = HashHelper.Current.ComputeHash(user.Password, salt);
                    userRepository.Add(user);
                }
                unitOfWork.Commit();
            }
            CacheManager.Current.ClearCache(CacheKey.AccountKeys.UserCategory);
        }

        public Role GetRole(string roleName)
        {
            return CacheManager.Current.FromCache(string.Format(CacheKey.AccountKeys.RoleName, SiteContext.Current.Site.Id, roleName), CacheKey.AccountKeys.RoleCategory,
                () => roleRepository.Get(roleName, SiteContext.Current.Site.Id));
        }

        public IList<Role> GetRolesForUser(User user)
        {
            if (user == null)
            {
                return CacheManager.Current.FromCache(string.Format(CacheKey.AccountKeys.RoleUnregisteredUsers, SiteContext.Current.Site.Id), () => GetDefaultRoles());
            }

            var roles = CacheManager.Current.GetFromCache(string.Format(CacheKey.AccountKeys.UserRoles, SiteContext.Current.Site.Id, user.Id), CacheKey.AccountKeys.UserRolesCategory) as IList<Role>;
            if (roles == null)
            {
                roles = roleRepository.GetAllByUser(user.Id);
                var unregisteredUsersRole = GetDefaultRoles();
                unregisteredUsersRole.ForEach(a => roles.Add(a));
                CacheManager.Current.AddToCache(string.Format(CacheKey.AccountKeys.UserRoles, SiteContext.Current.Site.Id, user.Id), CacheKey.AccountKeys.UserRolesCategory, roles);
            }
            return roles;
        }

        protected virtual List<Role> GetDefaultRoles()
        {
            var unregisteredUsersRole = GetRole(Constants.Roles.UnregisteredUsers);
            if (unregisteredUsersRole == null)
                throw new BusinessException(string.Format("Database is corrupted. The 'Unregistered users' role was not found for '{0}' site.", SiteContext.Current.Site.Name));
            return new List<Role>() { unregisteredUsersRole };
        }

        public IList<Permission> GetPermissions()
        {
            return permissionRepository.GetPermissions();
        }

        public IList<ControlPermission> GetControlPermissions(int pageContentId)
        {
            return permissionRepository.GetControlPermissions(pageContentId);
        }

        public void AddControlPermission(ControlPermission permission)
        {
            permissionRepository.AddControlPermission(permission);
        }

        public void DeleteControlPermission(int pageContentId, Guid permissionId, Guid roleId)
        {
            permissionRepository.DeleteControlPermission(pageContentId, permissionId, roleId);
        }

        public IList<PagePermission> GetPagePermissions(Guid pageId)
        {
            return permissionRepository.GetPagePermissions(pageId);
        }

        public void AddPagePermission(PagePermission permission)
        {
            permissionRepository.AddPagePermission(permission);
        }

        public void DeletePagePermission(Guid pageId, Guid permissionId, Guid roleId)
        {
            permissionRepository.DeletePagePermission(pageId, permissionId, roleId);
        }


        public IList<Role> GetRoles()
        {
            return roleRepository.GetAll(SiteContext.Current.Site.Id);
        }
    }
}
